﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.ObjectModel;

namespace Xceed.Silverlight.ListBox
{
  internal sealed class ViewCollection : ObservableCollection<View>
  {
    #region ActiveView Internal Property

    internal View ActiveView
    {
      get
      {
        return m_activeView;
      }
      private set
      {
        if( m_activeView != value )
        {
          m_activeView = value;

          if( m_deferPropertyChangedCount == 0 )
            this.RaiseActiveViewChanged();
        }
      }
    }

    private View m_activeView;

    internal event EventHandler ActiveViewChanged;

    private void RaiseActiveViewChanged()
    {
      if( this.ActiveViewChanged != null )
        this.ActiveViewChanged( this, EventArgs.Empty );
    }

    #endregion

    #region Protected Methods

    protected override void ClearItems()
    {
      IDisposable deferActiveViewPropertyChanged = null;

      if( this.ActiveView != null )
      {
        this.ActiveView = null;
        deferActiveViewPropertyChanged = this.DeferActiveViewPropertyChanged();
      }

      foreach( View view in this.Items )
      {
        view.IsActiveViewChanged -= this.OnViewIsActiveViewPropertyChanged;
      }

      base.ClearItems();

      if( deferActiveViewPropertyChanged != null )
        deferActiveViewPropertyChanged.Dispose();
    }

    protected override void InsertItem( int index, View item )
    {
      if( this.Contains( item ) )
        throw new InvalidOperationException( "Collection already contains this View." );

      item.IsActiveViewChanged += this.OnViewIsActiveViewPropertyChanged;

      IDisposable deferActiveViewPropertyChanged = null;

      if( item.IsActiveView )
      {
        deferActiveViewPropertyChanged = this.DeferActiveViewPropertyChanged();

        View activeView = this.ActiveView;

        if( activeView != null )
        {
          activeView.IsActiveView = false;
        }

        // Set the newly added view as the ActiveView
        this.ActiveView = item;
      }

      base.InsertItem( index, item );

      // Always consider the last added as ActiveView if not
      // explicitly specified on the view itself
      if( this.ActiveView == null || !this.ActiveView.IsActiveView )
      {
        this.ActiveView = item;
      }

      if( deferActiveViewPropertyChanged != null )
        deferActiveViewPropertyChanged.Dispose();
    }

    protected override void SetItem( int index, View item )
    {
      View indexItem = this[ index ];

      if( indexItem != item )
        indexItem.IsActiveViewChanged -= this.OnViewIsActiveViewPropertyChanged;

      if( item != null )
        item.IsActiveViewChanged += this.OnViewIsActiveViewPropertyChanged;

      IDisposable deferActiveViewPropertyChanged = null;

      if( item.IsActiveView )
      {
        deferActiveViewPropertyChanged = this.DeferActiveViewPropertyChanged();

        View activeView = this.ActiveView;

        if( activeView != null )
          activeView.IsActiveView = false;

        // Set the newly replaced view as the ActiveView
        this.ActiveView = item;
      }

      base.SetItem( index, item );

      // Always consider the last added as ActiveView if not
      // explicitly specified on the view itself
      if( this.ActiveView == null || !this.ActiveView.IsActiveView )
      {
        this.ActiveView = item;
      }

      if( deferActiveViewPropertyChanged != null )
        deferActiveViewPropertyChanged.Dispose();
    }

    protected override void RemoveItem( int index )
    {
      View itemAtIndex = this[ index ];

      IDisposable deferActiveViewPropertyChanged = null;

      if( itemAtIndex != null )
      {
        itemAtIndex.IsActiveViewChanged -= this.OnViewIsActiveViewPropertyChanged;

        if( itemAtIndex == this.ActiveView )
        {
          deferActiveViewPropertyChanged = this.DeferActiveViewPropertyChanged();

          // We set the previous view as the ActiveView
          if( index > 0 )
          {
            this.ActiveView = this[ index - 1 ];
          }
          else if( this.Count > 0 )
          {
            this.ActiveView = this[ 0 ];
          }
          else
          {
            this.ActiveView = null;
          }
        }
      }

      base.RemoveItem( index );

      if( deferActiveViewPropertyChanged != null )
        deferActiveViewPropertyChanged.Dispose();
    }

    #endregion

    #region Private Methods

    private IDisposable DeferActiveViewPropertyChanged()
    {
      return new DeferActiveViewPropertyChangedDisposable( this );
    }

    private void OnViewIsActiveViewPropertyChanged( object sender, EventArgs e )
    {
      // Already processing a View.IsActiveView property changed
      if( m_deferPropertyChangedCount > 0 )
        return;

      View view = sender as View;

      if( view == null )
        return;

      View activeView = this.ActiveView;

      IDisposable deferActiveViewPropertyChanged = null;

      // The view is now active, ensure
      // to set the old activeView to
      // inactive if any
      if( view.IsActiveView )
      {
        deferActiveViewPropertyChanged = this.DeferActiveViewPropertyChanged();

        if( activeView == null )
        {
          this.ActiveView = view;
        }
        else if( activeView != view )
        {
          activeView.IsActiveView = false;
          this.ActiveView = view;
        }
      }
      else
      {
        if( activeView != view )
          return;

        deferActiveViewPropertyChanged = this.DeferActiveViewPropertyChanged();

        // The view is now inactive.
        // Set the previous or next view 
        // as active if possible.
        int viewIndex = this.IndexOf( view );

        // The first item was the activeView
        if( viewIndex == 0 )
        {
          // It's the only view in the collection
          if( this.Count <= 1 )
          {
            this.ActiveView = null;
          }
          else
          {
            // Set the next view as active
            activeView = this[ 1 ];
            activeView.IsActiveView = true;
            this.ActiveView = activeView;
          }
        }
        else
        {
          // Set the previous view as active
          activeView = this[ viewIndex-- ];
          activeView.IsActiveView = true;
          this.ActiveView = activeView;
        }
      }

      if( deferActiveViewPropertyChanged != null )
        deferActiveViewPropertyChanged.Dispose();
    }

    #endregion

    #region Private Fields

    private int m_deferPropertyChangedCount;

    #endregion

    #region DeferActiveViewPropertyChangedDisposable Private Class

    private class DeferActiveViewPropertyChangedDisposable : IDisposable
    {
      public DeferActiveViewPropertyChangedDisposable( ViewCollection viewCollection )
      {
        m_viewCollection = viewCollection;
        m_viewCollection.m_deferPropertyChangedCount++;
      }

      #region IDisposable Members

      public void Dispose()
      {
        m_viewCollection.m_deferPropertyChangedCount--;

        if( m_viewCollection.m_deferPropertyChangedCount == 0 )
        {
          m_viewCollection.RaiseActiveViewChanged();
        }

        m_viewCollection = null;
      }

      #endregion

      private ViewCollection m_viewCollection;
    }

    #endregion
  }
}
